package com.lh.api.controller;

import com.lh.api.config.LHConfig;
import com.lh.api.config.MyConstants;
import com.lh.api.form.CodeLoginForm;
import com.lh.api.form.LoginForm;
import com.lh.api.form.RegisterForm;
import com.lh.api.service.UserService;
import com.lh.api.utils.JwtUtils;
import com.lh.api.utils.SmsUtils;
import com.lh.api.vo.LoginResponseVo;
import com.lh.common.entity.admin.User;
import com.lh.common.enums.IsDel;
import com.lh.common.enums.SmsTypeEnum;
import com.lh.common.enums.TrueFalseEnum;
import com.lh.common.enums.UserTypeEnum;
import com.lh.common.exception.RRException;
import com.lh.common.sms.SmsSendUtils;
import com.lh.common.utils.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.Date;
import java.util.UUID;

/**
 * APP登录授权
 *
 * @author Mark sunlightcs@gmail.com
 */
@RestController
@RequestMapping("/api/loginOrReg")
@Api(value="ApiLoginOrRegisterController",tags={"APP登录/注册相关接口"})
public class ApiLoginOrRegisterController extends AbstractController{
    @Autowired
    private UserService userService;

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private LHConfig lhConfig;

    @Value("${spring.profiles.active}")
    private String profile;


    @PostMapping("/login")
    @ApiOperation("账号+密码登录")
    public Result<LoginResponseVo> login(@RequestBody  @Validated  LoginForm form){
        AssertUtil.assertNotFalse(RegexUtils.checkMobile(form.getMobile()), Constant.RESULT.FI1000, Constant.MOBILE_NOT_MATCH);
        //用户登录
        User user = userService.login(form);
        AssertUtil.assertNotTrue(user == null, Constant.RESULT.FI1006, "Mobile No. and password do not match");

        //生成token
        String token = jwtUtils.generateToken(form.getMobile());
        String tokenCacheKey = MyConstants.TOKEN_CACHE.replace(MyConstants.USERID, user.getUid().toString());

        if(redisUtils.hasKey(tokenCacheKey)) {//删除已经登录用户
            redisUtils.delete(redisUtils.get(tokenCacheKey));
            redisUtils.delete(tokenCacheKey);
        }

        //缓存用户信息
        cacheUserInfo(user.getUid(),token,user);

        LoginResponseVo responseVo = new LoginResponseVo();
        responseVo.setToken(token);
        responseVo.setUser(user);
        responseVo.setExpire(jwtUtils.getExpire());
        return Result.ok(responseVo);
    }

    @PostMapping("/loginByCode")
    @ApiOperation("手机号+验证码登录")
    public Result<LoginResponseVo> loginByCode(@RequestBody @Validated CodeLoginForm form) {
        AssertUtil.assertNotFalse(RegexUtils.checkMobile(form.getMobile()), Constant.RESULT.FI1000, Constant.MOBILE_NOT_MATCH);
        //检查用户是否存在
        User user = userService.queryByMobile(form.getMobile());
        AssertUtil.assertNotNull(user, Constant.RESULT.FI1009, Constant.MOBILE_EXIST);
        //检验手机验证码是否存在
        String key = String.format(MyConstants.LOGIN_SMS_CODE_KEY,form.getMobile());
        if (!redisUtils.hasKey(key)) {
            throw new RRException(Constant.RESULT.FI1012,Constant.OTP_EXPIRED);
        }

        //检验手机验证码是否正确
        String code = redisUtils.get(key, String.class);
        if (!code.equals(form.getCode())) {
            throw new RRException(Constant.RESULT.FI1011,Constant.OTP_EXPIRED);
        }

        //生成token
        String token = jwtUtils.generateToken(form.getMobile());
        LoginResponseVo responseVo = new LoginResponseVo();

        String tokenCacheKey = MyConstants.TOKEN_CACHE.replace(MyConstants.USERID, user.getUid().toString());
        if(redisUtils.hasKey(tokenCacheKey)) {//删除已经登录用户
            redisUtils.delete(redisUtils.get(tokenCacheKey));
            redisUtils.delete(tokenCacheKey);
        }

        if(user != null){
            //缓存用户信息
            cacheUserInfo(user.getUid(), token,user);
        }
        responseVo.setUser(user);
        responseVo.setToken(token);
        responseVo.setExpire(jwtUtils.getExpire());
        return Result.ok(responseVo);
    }

    @ApiOperation(value = "用户手机号否存在")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType="query", name = "phone", value = "用户手机号", required = true, dataType = "String")
    })
    @GetMapping(value = "/isExitPhone")
    public Result<Boolean> exitUserName(String phone){
        AssertUtil.assertNotFalse(RegexUtils.checkMobile(phone), Constant.RESULT.FI1000, Constant.MOBILE_NOT_MATCH);
        User user = userService.queryByMobile(phone);
        boolean isExit = user != null;
        return Result.ok(isExit);
    }

    @ApiOperation(value = "用户退出")
    @GetMapping(value = "/logout")
    @ResponseBody
    public Result<String> logout(){
        User user = getUser();
        if(user == null) {
            return Result.ok();
        }
        String tokenCacheKey = MyConstants.TOKEN_CACHE.replace(MyConstants.USERID, user.getUid().toString());

        if(redisUtils.hasKey(tokenCacheKey)) {//删除已经登录用户
            redisUtils.delete(redisUtils.get(tokenCacheKey));
            redisUtils.delete(tokenCacheKey);
        }
        return Result.ok();
    }


    @PostMapping("/register")
    @ApiOperation("用户注册")
    @Transactional
    public Result<LoginResponseVo> register(@RequestBody @Validated RegisterForm form) {
        AssertUtil.assertNotFalse(RegexUtils.checkMobile(form.getMobile()), Constant.RESULT.FI1000, Constant.MOBILE_NOT_MATCH);
        //检测手机号是否已注册
        User user = userService.queryByMobile(form.getMobile());
        if(user != null){
            throw new RRException(Constant.RESULT.FI1005,"Mobile No.already exists");
        }

        //检验手机验证码是否存在, 检验手机验证码是否正确
        String key = String.format(MyConstants.REG_SMS_CODE_KEY,form.getMobile());
        String code = redisUtils.get(key, String.class);
        if (StringUtils.isEmpty(code) || !code.equals(form.getCode())) {
            throw new RRException(Constant.RESULT.FI1011,Constant.OTP_EXPIRED);
        }

        //保存用户
        user= new User();
        //随机14位字符串
        String uuid = UUID.randomUUID().toString().replace("-", "");
        user.setName(uuid.substring(0, 14));

        user.setPhone(form.getMobile());
        user.setType(UserTypeEnum.GENERAL);
        user.setStatus(TrueFalseEnum.TRUE);
        user.setIsDel(IsDel.NO);
        user.setPassword(DigestUtils.sha256Hex(form.getPassword()));
        user.setCreateTime(new Date());
        userService.save(user);
        //相同手机号的机器人失效
        userService.invalidRobot(user.getPhone());

        //生成token
        String token = jwtUtils.generateToken(form.getMobile());
        //缓存用户信息
        cacheUserInfo(user.getUid(), token,user);

        LoginResponseVo responseVo = new LoginResponseVo();
        responseVo.setToken(token);
        responseVo.setExpire(jwtUtils.getExpire());
        responseVo.setUser(user);
        return Result.ok(responseVo);
    }

    @GetMapping(value = "/sendCode")
    @ApiOperation(value = "发送验证码，开发环境不发送短信默认验证码123456")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "phone", value = "电话号码", required = true,dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "query", name = "smsType", value = "短信类型", required = true,dataTypeClass = SmsTypeEnum.class)
    })
    public Result<String> sendCode(String phone,SmsTypeEnum smsType){
        //校验手机号
        AssertUtil.assertNotTrue(StringUtils.isEmpty(phone), Constant.RESULT.FI1000, "Mobile No. is required");
        phone = phone.trim();
        //检查手机号是否正确
        AssertUtil.assertNotFalse(RegexUtils.checkMobile(phone), Constant.RESULT.FI1000, Constant.MOBILE_NOT_MATCH);

        User reusltObj = userService.queryByMobile(phone);
        if(SmsTypeEnum.LOGIN.equals(smsType) || SmsTypeEnum.FIND.equals(smsType)){
            AssertUtil.assertNotNull(reusltObj,Constant.RESULT.FI1000,Constant.MOBILE_EXIST);
        }

        //检查手机号当天发送次数
        String codeCountkey = String.format(MyConstants.SMS_CODE_DAILY_COUNT,phone);
        codeCountkey =  codeCountkey + ":" +DateTimeUtil.getDate().replace("-","");
        //如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 INCR 操作。
        //验证码一个用户一天最多发20次
        Long count = redisUtils.incr(codeCountkey,1);
        if(redisUtils.getExpire(codeCountkey) <0){
            redisUtils.expire(codeCountkey, DateTimeUtil.getTillTomorrowTime());
        }

        //校验次数
        if(count > lhConfig.getLoginSmsCodeMaxCount()){
            throw new RRException(Constant.RESULT.FI1004, "OTP sent too frequently");
        }

        String code = getRandom(6);

        Boolean bool = false;
        if(lhConfig.getIsRealSend()){
            if(!phone.startsWith("1") ){
                bool= SmsSendUtils.smsSendInt(phone, String.format(lhConfig.getSmsInternationalTemplate(),code), lhConfig.getSmsInternationalAccount(), lhConfig.getSmsInternationalSecret(), lhConfig.getSmsInternationalUrl());
            }else {
                bool= SmsSendUtils.smsSend(phone, String.format(lhConfig.getSmsCHTemplate(),code), lhConfig.getSmsCHAccount(), lhConfig.getSmsCHSecret(), lhConfig.getSmsCHUrl());
            }
        }else{
            bool = true;
            code="123456";
        }

        Result<String> result = null;
        if(bool){
            String key = SmsUtils.getRedisKey(smsType,phone);
            redisUtils.set(key,code,10*60);
            result = Result.ok("OTP sent successfully and is valid for 10 minutes");
        }
        else{
            //发送失败，发送数减1
            redisUtils.decr(codeCountkey,1);
            result = Result.error("OTP sent failed");
        }
        return result;

    }


    /**
     * 缓存用户信息
     * @param uid
     * @param user
     */
    protected void cacheUserInfo(Long uid, String token, User user){
        String tokenCacheKey = MyConstants.TOKEN_CACHE.replace(MyConstants.USERID, uid.toString());
        String userkey = String.format(MyConstants.USER_INFO_CACHE,token);

        redisUtils.set(tokenCacheKey,userkey,jwtUtils.getExpire() * 1000);
        redisUtils.set(userkey,user,jwtUtils.getExpire() * 1000);

    }


    @ApiOperation(value = "密码找回")
    @PostMapping(value = "/findPasswd")
    @Transactional
    public Result<String> findPasswd(@RequestBody @Validated RegisterForm registerForm){
        User user = userService.queryByMobile(registerForm.getMobile());
        AssertUtil.assertNotNull(user, Constant.RESULT.FI1009, Constant.MOBILE_EXIST);
        AssertUtil.assertNotFalse(!user.getPassword().equals(DigestUtils.sha256Hex(registerForm.getPassword())), Constant.RESULT.FI1000, "The new password cannot be the same as the original password");

        //检验手机验证码是否存在, 检验手机验证码是否正确
        String key = String.format(MyConstants.SECRET_SMS_CODE_KEY,registerForm.getMobile());
        String code = redisUtils.get(key, String.class);
        if (StringUtils.isEmpty(code) || !code.equals(registerForm.getCode())) {
            throw new RRException(Constant.RESULT.FI1011,Constant.OTP_EXPIRED);
        }

        User user1 = new User();
        user1.setUid(user.getUid());
        user1.setPassword(DigestUtils.sha256Hex(registerForm.getPassword()));
        userService.updateByKey(user1);
        return Result.ok();
    }


}
